﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;
using AppToolkit.WPF.ComponentModel;

namespace System.Collections.Specialized
{
    /// <summary>
    /// Propageates change notification from contained items.
    /// </summary>
    /// <typeparam name="TItem"></typeparam>
    public class ObservableCollectionEx<TItem> : ObservableCollection<TItem>, IItemChangedSupport<TItem>
    {
        /// <summary>
        /// default constructor
        /// </summary>
        public ObservableCollectionEx() 
            : base()
        {
        }

        /// <summary>
        /// Initializes a new instance of the ObservableCollectionEx class that contains 
        /// elements copied from the specified collection. 
        /// </summary>
        /// <param name="collection"></param>
        public ObservableCollectionEx(IEnumerable<TItem> collection)
            : base(collection)
        {
            hookAllItems();
        }

        /// <summary>
        /// Initializes a new instance of the ObservableCollectionEx class that contains 
        /// elements copied from the specified list. 
        /// </summary>
        /// <param name="list"></param>
        public ObservableCollectionEx(List<TItem> list)
            : base(list)
        {
            hookAllItems();
        }

        /// <summary>
        /// Override to hook each items change notification.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="item"></param>
        protected override void InsertItem(int index, TItem item)
        {
            base.InsertItem(index, item);
            tryHookItem(item);
        }

        /// <summary>
        /// Override to unhook each items change notification.
        /// </summary>
        /// <param name="index"></param>
        protected override void RemoveItem(int index)
        {
            var item = this[index];

            unhookItem(item);
            base.RemoveItem(index);
        }

        /// <summary>
        /// Removes all items from the collection.
        /// </summary>
        protected override void ClearItems()
        {
            foreach (var item in this)
            {
                unhookItem(item);
            }
            base.ClearItems();
        }

        private void OnPropertyChanged(object s, PropertyChangedEventArgs a)
        {
            var ea = new ItemChangedEventArgs<TItem>((TItem)s, a.PropertyName);
            if (ItemChanged != null)
            {
                ItemChanged(this, ea);
            }
        }

        private void hookAllItems()
        {
            foreach (var item in this)
            {
                tryHookItem(item);
            }
        }

        private void tryHookItem(TItem item)
        {
            INotifyPropertyChanged npc = item as INotifyPropertyChanged;
            if (npc != null)
            {
                npc.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
            }
        }

        private void unhookItem(TItem item)
        {
            INotifyPropertyChanged npc = item as INotifyPropertyChanged;
            if (npc != null)
            {
                npc.PropertyChanged -= new PropertyChangedEventHandler(OnPropertyChanged);
            }
        }

        #region IItemChangedSupport<TItem> Members
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<ItemChangedEventArgs<TItem>> ItemChanged;
        #endregion
    }
}
